﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Reflection;
using System.Windows;

namespace SmartPaginator
{
    public static class VisualExtensions
    {
        public static T ShallowClone<T>(this T original, bool isForSplitting) where T : ContainerVisual, new()
        {
            T copy = Activator.CreateInstance(original.GetType()) as T;
            copy.CacheMode = original.CacheMode;
            copy.Clip = original.Clip;
            copy.Effect = original.Effect;
            copy.Offset = original.Offset;
            copy.Opacity = original.Opacity;
            copy.OpacityMask = original.OpacityMask;
            copy.Transform = original.Transform;
            copy.XSnappingGuidelines = original.XSnappingGuidelines;
            copy.YSnappingGuidelines = original.YSnappingGuidelines;

            if (typeof(IShallowCloneable).IsAssignableFrom(original.GetType()))
                ((IShallowCloneable)copy).ShallowClone(original, isForSplitting);

            return copy;
        }

        public static T DeepClone<T>(this T original) where T : ContainerVisual, new()
        {
            var copy = original.ShallowClone(false);

            if (original is DrawingVisual)
                CopyDrawing(copy as DrawingVisual, original as DrawingVisual);

            foreach (var child in original.Children)
            {
                var childContainer = child as ContainerVisual;
                if (childContainer != null)
                {
                    copy.Children.Add(childContainer.DeepClone());
                    continue;
                }
            }
            return copy;
        }

        public static void CopyDrawing(DrawingVisual copy, DrawingVisual original)
        {
            if (original.Drawing != null)
            {
                using (var context = copy.RenderOpen())
                {
                    context.DrawDrawing(original.Drawing);
                }
            }
        }

        public interface IShallowCloneable
        {
            void ShallowClone(object original, bool isForSplitting);
        }

        public static bool IsGreaterThan(double v1, double v2)
        { 
            // the WPF engine allows for some rounding errors. for example, when we ask the textformatter to famrt text into a 720 width, 
            // it may return something like 720.000000000001. So we ignore differences smaller than our resolution (i.e. 300dpi)
            double difference = v1 - v2;
            return (difference > 0) &&
                   (Math.Abs(difference) >  1.0/300);
        }

        public static bool AreEqual(double v1, double v2)
        { 
            // the WPF engine allows for some rounding errors. for example, when we ask the textformatter to famrt text into a 720 width, 
            // it may return something like 720.000000000001. So we ignore differences smaller than our resolution (i.e. 300dpi)
            return Math.Abs(v1 - v2) <  1.0/300;
        }


        public static void Draw(this Visual visual, DrawingContext drawingContext)
        {
            Draw(visual, drawingContext, visual);
        }

        private static void Draw(Visual visual, DrawingContext drawingContext, Visual reference)
        {
            var drawing = VisualTreeHelper.GetDrawing(visual);
            if (drawing != null)
            {
                var offset = visual.TransformToAncestor(reference) as Transform;
                if (offset != null)
                    drawingContext.PushTransform(offset);
                drawingContext.DrawDrawing(drawing);
                if (offset != null)
                    drawingContext.Pop();
            }

            int childrenCount = VisualTreeHelper.GetChildrenCount(visual);

            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(visual, i) as Visual;
                if (child != null)
                    Draw(child, drawingContext, reference);
            }
        }

    }
}
